﻿using System.Collections.Generic;
using System.Linq;
using Fish.Utility;

namespace MassiveBattle.Controller
{
	public class BaseStat
	{
		public interface IStat { }

		public abstract class Stat<T> : IStat
		{
			public delegate void FGet(ref T point);

			protected IProtect<T> protect;

			private FGet eventGet = null;
			public event FGet EventGet
			{
				add
				{
					if (eventGet != null && eventGet.GetInvocationList().Contains(value))
						return;

					eventGet += value;
				}

				remove
				{
					eventGet -= value;
				}
			}

			public T Get(IProtectMonitor monitor)
			{
				var value = protect.Get(monitor);

				OnGet(ref value);

				return value;
			}

			private void OnGet(ref T value)
			{
				if (eventGet != null)
					eventGet(ref value);
			}

			protected abstract void Init(T value, IProtectMonitor monitor);
		}

		public class StatInt : Stat<int>
		{
			public static StatInt Create(int value, IProtectMonitor monitor)
			{
				var instance = new StatInt();

				instance.Init(value, monitor);

				return instance;
			}

			protected override void Init(int value, IProtectMonitor monitor)
			{
				protect = ProtectInt.Create(value, monitor);
			}
		}

		public class StatFloat : Stat<float>
		{
			public static StatFloat Create(float value, IProtectMonitor monitor)
			{
				var instance = new StatFloat();

				instance.Init(value, monitor);

				return instance;
			}

			protected override void Init(float value, IProtectMonitor monitor)
			{
				protect = ProtectFloat.Create(value, monitor);
			}
		}

		protected IDictionary<AptitudeID_New, IStat> dataDic = new Dictionary<AptitudeID_New, IStat>();

		public int Level { get; set; }

		public Stat<int> atkType { get { return dataDic[AptitudeID_New.atkType] as Stat<int>; } }
		public Stat<int> baseAtk { get { return dataDic[AptitudeID_New.baseAtk] as Stat<int>; } }
		public Stat<int> growAtk { get { return dataDic[AptitudeID_New.growAtk] as Stat<int>; } }
		public Stat<int> assist { get { return dataDic[AptitudeID_New.assist] as Stat<int>; } }
		public Stat<int> baseHP { get { return dataDic[AptitudeID_New.baseHP] as Stat<int>; } }
		public Stat<int> growHP { get { return dataDic[AptitudeID_New.growHP] as Stat<int>; } }
		public Stat<int> baseDef { get { return dataDic[AptitudeID_New.baseDef] as Stat<int>; } }
		public Stat<int> growDef { get { return dataDic[AptitudeID_New.growDef] as Stat<int>; } }
		public Stat<int> init { get { return dataDic[AptitudeID_New.init] as Stat<int>; } }
		public Stat<int> taunt { get { return dataDic[AptitudeID_New.taunt] as Stat<int>; } }
		public Stat<int> melee { get { return dataDic[AptitudeID_New.melee] as Stat<int>; } }
		public Stat<int> growMelee { get { return dataDic[AptitudeID_New.growMelee] as Stat<int>; } }
		public Stat<int> range { get { return dataDic[AptitudeID_New.range] as Stat<int>; } }
		public Stat<int> growRange { get { return dataDic[AptitudeID_New.growRange] as Stat<int>; } }
		public Stat<int> magic { get { return dataDic[AptitudeID_New.magic] as Stat<int>; } }
		public Stat<int> growMagic { get { return dataDic[AptitudeID_New.growMagic] as Stat<int>; } }
		public Stat<int> pulse { get { return dataDic[AptitudeID_New.pulse] as Stat<int>; } }
		public Stat<int> growPulse { get { return dataDic[AptitudeID_New.growPulse] as Stat<int>; } }
		public Stat<int> special { get { return dataDic[AptitudeID_New.special] as Stat<int>; } }
		public Stat<int> growSpecial { get { return dataDic[AptitudeID_New.growSpecial] as Stat<int>; } }
		public Stat<float> evade { get { return dataDic[AptitudeID_New.evade] as Stat<float>; } }
		public Stat<int> evadeMod { get { return dataDic[AptitudeID_New.evadeMod] as Stat<int>; } }
		public Stat<float> defDamage { get { return dataDic[AptitudeID_New.defDamage] as Stat<float>; } }
		public Stat<int> defDamageMod { get { return dataDic[AptitudeID_New.defDamageMod] as Stat<int>; } }
		public Stat<int> HPMod { get { return dataDic[AptitudeID_New.HPMod] as Stat<int>; } }
		public Stat<int> healRate { get { return dataDic[AptitudeID_New.healRate] as Stat<int>; } }
		public Stat<int> res { get { return dataDic[AptitudeID_New.res] as Stat<int>; } }
		public Stat<int> skillRate { get { return dataDic[AptitudeID_New.skillRate] as Stat<int>; } }
		public Stat<int> skillRateMod { get { return dataDic[AptitudeID_New.skillRateMod] as Stat<int>; } }
		public Stat<int> critRate { get { return dataDic[AptitudeID_New.critRate] as Stat<int>; } }
		public Stat<int> critPower { get { return dataDic[AptitudeID_New.critPower] as Stat<int>; } }
		public Stat<int> attackAssist { get { return dataDic[AptitudeID_New.attackAssist] as Stat<int>; } }
		public Stat<int> defAssist { get { return dataDic[AptitudeID_New.defAssist] as Stat<int>; } }
		public Stat<int> atkInit { get { return dataDic[AptitudeID_New.atkInit] as Stat<int>; } }
		public Stat<float> atkCooltime { get { return dataDic[AptitudeID_New.atkCooltime] as Stat<float>; } }
		public Stat<int> atkCooltimeMod { get { return dataDic[AptitudeID_New.atkCooltimeMod] as Stat<int>; } }
		public Stat<int> moveTime { get { return dataDic[AptitudeID_New.moveTime] as Stat<int>; } }
		public Stat<int> moveTimeMod { get { return dataDic[AptitudeID_New.moveTimeMod] as Stat<int>; } }
		public Stat<int> getFromAtk { get { return dataDic[AptitudeID_New.getFromAtk] as Stat<int>; } }
		public Stat<int> getFromHit { get { return dataDic[AptitudeID_New.getFromHit] as Stat<int>; } }
		public Stat<int> moveSpd { get { return dataDic[AptitudeID_New.moveSpd] as Stat<int>; } }
		public Stat<int> moveSpdMod { get { return dataDic[AptitudeID_New.moveSpdMod] as Stat<int>; } }
		public Stat<float> healPower { get { return dataDic[AptitudeID_New.healPower] as Stat<float>; } }
		public Stat<int> healPowerMod { get { return dataDic[AptitudeID_New.healPowerMod] as Stat<int>; } }

		protected void Init(DBaseStat data, IProtectMonitor monitor)
		{
			foreach (var stat in data)
			{
				if (stat.Value is short)
					dataDic[stat.Key] = StatInt.Create((short)stat.Value, monitor);
				else if (stat.Value is float)
					dataDic[stat.Key] = StatFloat.Create((float)stat.Value, monitor);
			}
		}
	}

	public class MemberStat : BaseStat
	{
		private MemberStat(UnitStat data, IProtectMonitor monitor)
		{
			Init(data, monitor);
		}

		private void Init(UnitStat data, IProtectMonitor monitor)
		{
			Level = data.level;

			Init(data.data, monitor);
		}

		public static MemberStat Create(UnitStat data, IProtectMonitor monitor)
		{
			return new MemberStat(data, monitor);
		}
	}

	public class BossStat : BaseStat
	{
		public IProtect<int> phaseHP { get; private set; }
		public short phaseSet { get; private set; }
		public IProtect<int> enableAtkType { get; private set; }
		public IProtect<int> bossTrait { get; private set; }
		public IProtect<int> atkRadius { get; private set; }
		public IProtect<int>[] weakpoint { get; private set; }

		private BossStat(DBoss_New data, int level, IProtectMonitor monitor)
		{
			Init(data, level, monitor);
		}

		private void Init(DBoss_New data, int level, IProtectMonitor monitor)
		{
			int typeFlag = 0;

			for (int i = 0; i < data.enableAtkType.Length; i++)
			{
				if (data.enableAtkType[i] != 0)
					typeFlag |= 1 << data.enableAtkType[i];
			}

			Level = level;

			phaseHP = ProtectInt.Create(data.phaseHP, monitor);
			phaseSet = data.phaseSet;
			enableAtkType = ProtectInt.Create(typeFlag, monitor);
			bossTrait = ProtectInt.Create(data.bossTrait, monitor);
			atkRadius = ProtectInt.Create(data.atkRadius, monitor);
			weakpoint = new IProtect<int>[data.weakpoint.Length];
			for (int i = 0; i < weakpoint.Length; i++)
				weakpoint[i] = ProtectInt.Create(data.weakpoint[i], monitor);

			Init(data, monitor);
		}

		public static BossStat Create(DBoss_New data, int level, IProtectMonitor monitor)
		{
			return new BossStat(data, level, monitor);
		}
	}
}
